import datetime
import os
import time

import paramiko

from threading import Thread

from adminlayui import settings

import json

from app01 import models

data_list = {}



try:
    terminal_exipry_time = settings.CUSTOM_TERMINAL_EXIPRY_TIME
except Exception:
    terminal_exipry_time = 60 * 15


class SSH:
    def __init__(self, websocker, message, host, user,start_time):
        self.host = host
        self.user = user
        self.websocker = websocker
        self.message = message
        self.start_time = start_time
        self.cmd = ''
        self.res = ''


        self.time = time.time()  # 获取起始时间戳

        self.date_time = datetime.datetime.now().strftime('%Y-%m-%d_%H%M')
        self.msg_list =  []


    def record_webssh(self, host, user, type, data_list):
        try:
            tmp_date1 = time.strftime("%Y-%m-%d", time.localtime(int(self.time)))  # 用于记录开始时间

            record_dir_path = settings.RECORD_DIR + '/' + tmp_date1 + '/'
            if not os.path.exists(record_dir_path):
                os.makedirs(record_dir_path)
            record_filename = '%s_%s_%s.cast' % (self.date_time, host, user)  # 命名录像文件名
            record_filename_path = os.path.join(record_dir_path, record_filename)
            #print(record_filename_path)
            if type == 'header':  # 是否是头部header内容，只写入一次头部header内容
                with open(record_filename_path, 'w') as f:
                    f.write(json.dumps(data_list) + '\n')
            else:
                with open(record_filename_path, 'a',
                          buffering=1) as f:  # self.msg_list 必须为列表，如果使用字典格式会出现很多问题，已彩坑1天，换回列表就好了
                    for data in self.msg_list:
                        now_time = data[0]
                        message = data[1]
                        iodata = [now_time - self.time, 'o', message]  # 生成数据流格式内容
                        f.write((json.dumps(iodata) + '\n'))  # 写入执行输出输入的内容数据流

        except Exception as e:
            print(e)
            print('异常文件：%s ,异常行号：%s' % (e.__traceback__.tb_frame.f_globals['__file__'], e.__traceback__.tb_lineno))
        finally:
            static_time_exist = models.LuXiangVideo.objects.filter(start_time=self.start_time).exists()
            if static_time_exist:
                pass
            else:
                models.LuXiangVideo.objects.create(start_time=self.start_time, video_lx=record_filename_path)

    # term 可以使用 ansi, linux, vt100, xterm, dumb，除了 dumb外其他都有颜色显示
    def connect(self, host, user, password=None, ssh_key=None, port=22, timeout=30,
                term='ansi', pty_width=80, pty_height=24):
        global data_list
        try:
            ssh_client = paramiko.SSHClient()
            ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

            ssh_client.connect(username=user, password=password, hostname=host, port=port, timeout=timeout)

            transport = ssh_client.get_transport()
            self.channel = transport.open_session()
            self.channel.get_pty(term=term, width=pty_width, height=pty_height)
            self.channel.invoke_shell()

            # 如果socket连接在指定时间内无数据交互会断开，原理就是读取socket连接，如果指定时间内无返回就抛出异常
            # 需要注意：当在终端上运行无输入内容 但是会阻塞当前阻断的程序时 如果超过这个时间也会被断开
            self.channel.settimeout(terminal_exipry_time)  # 15分钟

            # 构建录像文件header
            header_data = {
                "version": 2,
                "width": 250,
                "height": 57,
                "timestamp": self.time,
                "env": {
                    "SHELL": "/bin/bash",
                    "TERM": term
                },
                "title": "boamp_webssh_record"
            }
            self.record_webssh(host, user, 'header', header_data)
            # 连接建立一次，之后交互数据不会再进入该方法


            for i in range(2):
                recv = self.channel.recv(65535).decode('utf-8')
                self.message['status'] = 0
                self.message['message'] = recv
                message = json.dumps(self.message)
                self.websocker.send(message)
                self.res += recv


                #### 录像
                now_time = time.time()
                data_list_temp = [now_time, recv]
                self.msg_list.append(data_list_temp)
                # print(data_list)
                self.record_webssh(host, user, 'iodata', data_list)
                self.msg_list = []



            # 创建3个线程将服务器返回的数据发送到django websocket（1个线程都可以）
            Thread(target=self.websocket_to_django).start()
            # Thread(target=self.websocket_to_django).start()
            # Thread(target=self.websocket_to_django).start()
        except:
            self.message['status'] = 2
            self.message['message'] = 'connection faild...'
            message = json.dumps(self.message)
            self.websocker.send(message)
            self.websocker.close(3001)

    def resize_pty(self, cols, rows):
        self.channel.resize_pty(width=cols, height=rows)

    def django_to_ssh(self, data):
        try:
            self.channel.send(data)

            if data == '\r':
                data = '\n <br>'  # <br> 为了让命令 在html上 有换行的效果

            if data == '' or data == '' or data == '' or data == '[2;2R':
                data = ''

            if data == '[A' or data == '[B':
                data = '执行历史命令~'
            #  方向键  上 [A   下[B
            #   ctrl + c
            #   ctrl + z
            #   ctrl + x

            # vi 编辑的时候 一些特殊的 符号  [2;2Rdd:wq!

            if data == '':
                data = '退出vim编辑 '

            if data == '':
                data = ''  # 因为在webssh上 有删除 行为的时候，在python代码上会 生成一个 '' 图标，这里把删除图标 设置为空
                self.cmd = self.cmd[:-1]  # 这里是，当在webssh上执行 删除字母的行为时，在cmd上相应的删除最后一个字母，这样保存下来的，就是最后用户 运行的命令

            self.cmd += data

        except Exception:
            self.close()

    def django_bytes_to_ssh(self, data):
        try:
            self.channel.send(data)
        except Exception:
            self.close()

    def websocket_to_django(self):
        global data_list
        try:
            while True:

                        # data = data.decode('utf-8')
                        data = self.channel.recv(1024).decode('utf-8', 'ignore')
                        if len(data) != 0:
                            self.message['status'] = 0
                            self.message['message'] = data
                            self.res += data
                            message = json.dumps(self.message)
                            self.websocker.send(message)
                            # print("===================", len(self.msg_list))
                            now_time = time.time()
                            data_list_temp = [now_time, data]
                            if len(self.msg_list) < 60:  # 判断数据列表长度，60个内容就写入一次文件，避免了频繁写入文件，消耗io导致数据丢失的问题
                                self.msg_list.append(data_list_temp)
                            else:
                                self.msg_list.append(data_list_temp)
                                self.record_webssh(self.host, self.user, 'iodata', data_list)
                                self.msg_list = []
                        else:
                            return



        except:
            self.close()

    def close(self):
        global data_list
        #print(self.message['message'])
        self.message['status'] = 1
        self.message['message'] = 'connection closed...'
        message = json.dumps(self.message)

        ####录像
        now_time = time.time()
        # print(self.message['message'])
        data_list_temp = [now_time, self.message['message']]
        self.msg_list.append(data_list_temp)
        self.record_webssh(self.host, self.user, 'iodata', data_list)

        self.websocker.send(message)
        self.websocker.close()
        self.channel.close()



    def shell(self, data):
        # 原作者使用创建线程的方式发送数据到ssh，每次发送都是一个字符，可以不用线程
        # 直接调用函数性能更好
        # Thread(target=self.django_to_ssh, args=(data,)).start()
        self.django_to_ssh(data)

        # 原作者将发送数据到django websocket的线程创建函数如果写到这，会导致每在客户端输入一个字符就创建一个线程
        # 最终可能导致线程创建太多，故将其写到 connect 函数中
        # Thread(target=self.websocket_to_django).start()

